///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file input.cpp
///
/// \brief   file holding implementations of functions associated with reading user input
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#include <src/pcps.h>
#include <src/input.h>

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Constructs the object that holds user input data.
///
///   Initializes data members to their default values, reads input lines from the specified file,
///   converts the lines into input commands, uses the commands to set the values of the class's
///   data members, and checks the input for sanity.
///
/// \param[in]     argc   The number of command line arguments passed to the program.
/// \param[in]     argv   The command line arguments (the input file name is assumed to be the
///                       second argument.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
pcps::Input::Input(int argc, char * argv[])

  // set data members' default values
  : _lattice_length_x(1),
    _lattice_length_y(1),
    _lattice_length_z(1),
    _n_cor_orb(0),
    _n_act_orb(0),
    _lattice_type(pcps::keyword::Lattice_Cartesian),
    _nparticles(2),
    _bfgs_length(20),
    _dump_freq(1),
    _print_freq(1),
    _arena_block_size(10000000),
    _arena_buffer_size(1000000),
    _nthreads(1),
    _global_seed(-1),
    _walkers_per_process(100),
    _pop_control_freq(10),
    _sr_disk_block_size(100),
    _sample_length(10000),
    _burn_in_length(10000),
    _properties_sample_length(10000),
    _begin_true_projector(-1),
    _particle_spin(0.5),
    _z_spin(0.0),
    _damping(0.0),
    _step_size(0.1),
    _max_grad_norm(1.0e100),
    _overlap_shift(0.001),
    _cg_thresh(1e-6),
    _fci_shift(0.0),
    _trial_energy(0.0),
    _pmc_tau(0.001),
    _tei_screening(1e-9),
    _init_config_thresh(-1000.0),
    _uhf_desegragation(0.1),
    _uhf_bias_decay(1.0),
    _correlator_size(2),
    _correlator_type(pcps::keyword::Correlator_Line),
    _particle_type(pcps::keyword::Part_Fermion),
    _ref_type(pcps::keyword::Ref_Uniform),
    _scalar_type(pcps::keyword::Scalar_Real),
    _boundary(pcps::keyword::Boundary_Open),
    _hamiltonian_type(pcps::keyword::Ham_Hubbard),
    _hubbard_t(0.0),
    _hubbard_u(0.0),
    _heisenberg_b(0.0),
    _workdir("./"),
    _optimization(pcps::keyword::Opt_None),
    _move_type(pcps::keyword::Move_Standard),
    _orb_source(pcps::keyword::Orb_Source_None),
    _agp_source(pcps::keyword::AGP_Source_None),
    _pairing_source(pcps::keyword::Pairing_Source_None),
    _parallel_type(pcps::keyword::Parallel_Site),
    _max_macro_loop(20),
    _max_nr_iter(0),
    _max_ac_length(0),
    _max_hf_iter(0),
    _max_cg_iter(100),
    _max_cg_macro_iter(10),
    _energy_tolerance(1.0e-7),
    _verbose_print(false),
    _do_perturb(false),
    _properties(false),
    _fix_classical(false),
    _join_trotter_clusters(false),
    _random_correlators(false),
    _optimize_corrs(true),
    _optimize_agp_weights(false),
    _optimize_orb_coeffs(false),
    _optimize_pairing_matrix(false),
    _symmetric_pairing(false),
    _spin_to_fermion_dump(false),
    _project_sz(true),
    _precondition_sr(false),
    _sr_use_disk(false),
    _slightly_random_orbs(false),
    _active_energy_only(false),
    _s_half_orth(false),
    _spin_pair_correlations(false),
    _ssss_correlations(false),
    _spin_triple_products(false),
    _spin_triangle_3sz(false),
    _init_config(0,0),
    _uhf_bias(1, 0.0),
    _corr_sources()

{

  // get MPI info
  const MPI::Comm & comm = MPI::COMM_WORLD;
  const int nproc = comm.Get_size();
  const int myrank = comm.Get_rank();

  // create a vector to hold the lines of the input file
  std::vector<std::string> lines;

  // create a vector to hold the characters of user input commands
  std::vector<char> cmd;

  // read commands from the input file on the root process
  if (myrank == 0) {

    // check that an input file name was supplied as a command line argument
    if (argc < 2)
      throw pcps::Exception("the pcps program requires the input file name as its argument");

    // open the input file
    std::ifstream input_file(argv[1]);

    // check that the file opened successfully
    if (!input_file.is_open())
      throw pcps::Exception("failed to open input file \"%s\"") % argv[1];

    // print a greeting for the user input section of the output file
    std::cout << "*********************************** Begin User Input ***********************************" << std::endl;
    std::cout << std::endl;

    // read and store the lines of the input file
    while (!input_file.eof()) {

      // create a string to hold the next line
      std::string line;

      // read the line (this discards the end-of-line character)
      std::getline(input_file, line);

      // remove comments from the line (all text after a % character)
      std::stringstream ss(line);
      line.clear();
      std::getline(ss, line, '%');

      // put in the end-of-line character
      line.push_back('\n');

      // print the line
      std::cout << boost::format("line %4i:   %s") % (lines.size()+1) % line;

      // ignore equal signs by replacing them with spaces
      for (std::string::iterator c = line.begin(); c != line.end(); c++)
        if (*c == '=')
          *c = ' ';

      // store the line
      lines.push_back(line);

    }

    // print a banner ending the user input section of the output file
    std::cout << std::endl;
    std::cout << "***********************************  End User Input  ***********************************" << std::endl;
    std::cout << std::endl;

    // close the input file
    input_file.close();

    // read and process commands from the user's input, which are separated by ';' characters
    for (int i = 0; i < lines.size(); i++) {

      // convert the input line into a string stream
      std::stringstream ss(lines.at(i));

      // read characters from the stream
      while (true) {

        // read a character
        char c; ss.get(c);

        // if the stream is exhausted, move to the next line
        if (ss.eof()) break;

        // if we have reached the end of a command, process it
        if (c == ';') {

          // process the command (root node first, so it throws any errors)
          this->process_input_command(cmd, i+1);

          // send the command to the other processes
          this->bcast_input_command(cmd, i+1);

          // clear the characters in preparation for the next command
          cmd.clear();

        // otherwise, append the command with the character
        } else {

          cmd.push_back(c);

        }

      }

    }

    // process the final command
    this->process_input_command(cmd, lines.size()-1);

    // send the final command to the other processes
    this->bcast_input_command(cmd, lines.size()-1);

    // signal the other processes to stop receiving input data
    int stop_signal = -1;
    comm.Bcast(&stop_signal, 1, MPI::INT, 0);

    // verify that the input is sane
    this->verify();

  // slave processes
  } else {

    // receive input commands from the root process
    while (true) {

      // receive the length of the command
      int length;
      comm.Bcast(&length, 1, MPI::INT, 0);

      // if the stop signal was sent, exit the loop
      if (length < 0) break;

      // receive the line number on which the command ended
      int line_num;
      comm.Bcast(&line_num, 1, MPI::INT, 0);

      // resize the vector that will hold the command
      cmd.resize(length);

      // receive the command
      if (length > 0)
        comm.Bcast(&cmd.at(0), length, MPI::CHAR, 0);

      // process the command
      this->process_input_command(cmd, line_num);

    }

  }

  // prepare derived data
  this->initialize_derived_data();

  // print out a summary of the input
  if (myrank == 0) {
    std::cout << std::endl;
    std::cout << boost::format("pcps program is being run with the following options:") << std::endl;
    std::cout << std::endl;
    std::cout << boost::format("                    workdir = ") << _workdir << std::endl;
    std::cout << boost::format("              parallel_type = ") << _parallel_type << std::endl;
    std::cout << boost::format("               corr_sources =");
    for (std::set<std::string>::const_iterator s = _corr_sources.begin(); s != _corr_sources.end(); s++)
      std::cout << " " << *s;
    std::cout << std::endl;
    std::cout << boost::format("                 orb_source = ") << _orb_source << std::endl;
    std::cout << boost::format("       slightly_random_orbs = ") << ( _slightly_random_orbs ? "true" : "false" ) << std::endl;
    std::cout << boost::format("                 agp_source = ") << _agp_source << std::endl;
    std::cout << boost::format("             pairing_source = ") << _pairing_source << std::endl;
    std::cout << boost::format("               optimization = ") << _optimization << std::endl;
    std::cout << boost::format("                   boundary = ") << _boundary << std::endl;
    std::cout << boost::format("           hamiltonian_type = ") << _hamiltonian_type << std::endl;
    std::cout << boost::format("                s_half_orth = ") << ( _s_half_orth ? "true" : "false" ) << std::endl;
    std::cout << boost::format("                  hubbard_t = ") << _hubbard_t << std::endl;
    std::cout << boost::format("                  hubbard_u = ") << _hubbard_u << std::endl;
    std::cout << boost::format("               heisenberg_b = ") << _heisenberg_b << std::endl;
    std::cout << boost::format("         active_energy_only = ") << ( _active_energy_only ? "true" : "false" ) << std::endl;
    std::cout << boost::format("                 nparticles = ") << _nparticles << std::endl;
    std::cout << boost::format("              particle_type = ") << _particle_type << std::endl;
    std::cout << boost::format("              particle_spin = ") << _particle_spin << std::endl;
    std::cout << boost::format("                     z_spin = ") << _z_spin << std::endl;
    std::cout << boost::format("                 project_sz = ") << ( _project_sz ? "true" : "false" ) << std::endl;
    std::cout << boost::format("                   ref_type = ") << _ref_type << std::endl;
    std::cout << boost::format("                scalar_type = ") << _scalar_type << std::endl;
    std::cout << boost::format("               lattice_type = ") << _lattice_type << std::endl;
    std::cout << boost::format("           lattice_length_x = ") << _lattice_length_x << std::endl;
    std::cout << boost::format("           lattice_length_y = ") << _lattice_length_y << std::endl;
    std::cout << boost::format("           lattice_length_z = ") << _lattice_length_z << std::endl;
    std::cout << boost::format("                  n_cor_orb = ") << _n_cor_orb << std::endl;
    std::cout << boost::format("                  n_act_orb = ") << _n_act_orb << std::endl;
    std::cout << boost::format("            correlator_type = ") << _correlator_type << std::endl;
    std::cout << boost::format("            correlator_size = ") << _correlator_size << std::endl;
    std::cout << boost::format("         random_correlators = ") << ( _random_correlators ? "true" : "false" ) << std::endl;
    std::cout << boost::format("              fix_classical = ") << ( _fix_classical ? "true" : "false" ) << std::endl;
    std::cout << boost::format("             optimize_corrs = ") << ( _optimize_corrs ? "true" : "false" ) << std::endl;
    std::cout << boost::format("       optimize_agp_weights = ") << ( _optimize_agp_weights ? "true" : "false" ) << std::endl;
    std::cout << boost::format("        optimize_orb_coeffs = ") << ( _optimize_orb_coeffs ? "true" : "false" ) << std::endl;
    std::cout << boost::format("    optimize_pairing_matrix = ") << ( _optimize_pairing_matrix ? "true" : "false" ) << std::endl;
    std::cout << boost::format("          symmetric_pairing = ") << ( _symmetric_pairing ? "true" : "false" ) << std::endl;
    std::cout << boost::format("                max_hf_iter = ") << _max_hf_iter << std::endl;
    std::cout << boost::format("             max_macro_loop = ") << _max_macro_loop << std::endl;
    std::cout << boost::format("                max_nr_iter = ") << _max_nr_iter << std::endl;
    std::cout << boost::format("                    damping = %.6e") % _damping << std::endl;
    std::cout << boost::format("           energy_tolerance = %.6e") % _energy_tolerance << std::endl;
    std::cout << boost::format("                  step_size = ") << _step_size << std::endl;
    std::cout << boost::format("                bfgs_length = ") << _bfgs_length << std::endl;
    std::cout << boost::format("                  move_type = ") << _move_type << std::endl;
    std::cout << boost::format("              sample_length = ") << _sample_length << std::endl;
    std::cout << boost::format("             burn_in_length = ") << _burn_in_length << std::endl;
    std::cout << boost::format("              max_ac_length = ") << _max_ac_length << std::endl;
    std::cout << boost::format("              max_grad_norm = ") << _max_grad_norm << std::endl;
    std::cout << boost::format("                  dump_freq = ") << _dump_freq << std::endl;
    std::cout << boost::format("                 print_freq = ") << _print_freq << std::endl;
    std::cout << boost::format("            precondition_sr = ") << ( _precondition_sr ? "true" : "false" ) << std::endl;
    std::cout << boost::format("              overlap_shift = ") << _overlap_shift << std::endl;
    std::cout << boost::format("                  cg_thresh = ") << _cg_thresh << std::endl;
    std::cout << boost::format("                max_cg_iter = ") << _max_cg_iter << std::endl;
    std::cout << boost::format("          max_cg_macro_iter = ") << _max_cg_macro_iter << std::endl;
    std::cout << boost::format("                  fci_shift = ") << _fci_shift << std::endl;
    std::cout << boost::format("               trial_energy = %.12f") % _trial_energy << std::endl;
    std::cout << boost::format("                    pmc_tau = ") << _pmc_tau << std::endl;
    std::cout << boost::format("        walkers_per_process = ") << _walkers_per_process << std::endl;
    std::cout << boost::format("           pop_control_freq = ") << _pop_control_freq << std::endl;
    std::cout << boost::format("       begin_true_projector = ") << _begin_true_projector << std::endl;
    std::cout << boost::format("         sr_disk_block_size = ") << _sr_disk_block_size << std::endl;
    std::cout << boost::format("                sr_use_disk = ") << ( _sr_use_disk ? "true" : "false" ) << std::endl;
    std::cout << boost::format("      join_trotter_clusters = ") << ( _join_trotter_clusters ? "true" : "false" ) << std::endl;
    std::cout << boost::format("              verbose_print = ") << ( _verbose_print ? "true" : "false" ) << std::endl;
    std::cout << boost::format("       spin_to_fermion_dump = ") << ( _spin_to_fermion_dump ? "true" : "false" ) << std::endl;
    std::cout << boost::format("                 do_perturb = ") << ( _do_perturb ? "true" : "false" ) << std::endl;
    std::cout << boost::format("                 properties = ") << ( _properties ? "true" : "false" ) << std::endl;
    std::cout << boost::format("   properties_sample_length = ") << _properties_sample_length << std::endl;
    std::cout << boost::format("     spin_pair_correlations = ") << ( _spin_pair_correlations ? "true" : "false" ) << std::endl;
    std::cout << boost::format("          ssss_correlations = ") << ( _ssss_correlations ? "true" : "false" ) << std::endl;
    std::cout << boost::format("       spin_triple_products = ") << ( _spin_triple_products ? "true" : "false" ) << std::endl;
    std::cout << boost::format("          spin_triangle_3sz = ") << ( _spin_triangle_3sz ? "true" : "false" ) << std::endl;
    std::cout << boost::format("              tei_screening = ") << _tei_screening << std::endl;
    std::cout << boost::format("                   nthreads = ") << _nthreads << std::endl;
    std::cout << boost::format("           arena_block_size = ") << _arena_block_size << std::endl;
    std::cout << boost::format("          arena_buffer_size = ") << _arena_buffer_size << std::endl;
    std::cout << boost::format("                global_seed = ") << _global_seed << std::endl;
    std::cout << boost::format("                   uhf_bias = ");
    for (int i = 0; i < _uhf_bias.size(); i++)
      std::cout << boost::format(" %5.2f") % _uhf_bias.at(i);
    std::cout << std::endl;
    std::cout << boost::format("             uhf_bias_decay = ") << _uhf_bias_decay << std::endl;
    std::cout << boost::format("          uhf_desegragation = ") << _uhf_desegragation << std::endl;
    std::cout << boost::format("         init_config_thresh = ") << _init_config_thresh << std::endl;
    std::cout << boost::format("                init_config = ");
    for (int i = 0; i < _init_config.size(); i++)
      std::cout << boost::format(" %i") % _init_config.at(i);
    std::cout << std::endl;
    std::cout << std::endl;
    std::cout << boost::format("Executing in %s parallel mode with %i processes, each running %i threads.")
                 % _parallel_type % nproc % _nthreads << std::endl;
    std::cout << std::endl;
    std::cout << boost::format("Timings have an accuracy of %.1e seconds.") % MPI::Wtick() << std::endl;
    std::cout << std::endl;
  }

}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Checks that the user input options are sane.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::Input::verify() {

  // verify optimization type
  if (    _optimization != pcps::keyword::Opt_BFGS
       && _optimization != pcps::keyword::Opt_Energy_Evaluation
       && _optimization != pcps::keyword::Opt_Steepest_Descent
       && _optimization != pcps::keyword::Opt_Projected
       && _optimization != pcps::keyword::Opt_Projected_TI
       && _optimization != pcps::keyword::Opt_Imaginary_Time
       && _optimization != pcps::keyword::Opt_Stochastic_Imaginary_Time
       && _optimization != pcps::keyword::Opt_Real_Time
       && _optimization != pcps::keyword::Opt_Full_CI
       && _optimization != pcps::keyword::Opt_PMC
       && _optimization != pcps::keyword::Opt_Dump_DMRG_Ints
       && _optimization != pcps::keyword::Opt_None      )
    throw pcps::Exception( (boost::format("keyword \"optimization\" must be \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", or \"%s\".")
                            % pcps::keyword::Opt_BFGS
                            % pcps::keyword::Opt_Energy_Evaluation
                            % pcps::keyword::Opt_Steepest_Descent
                            % pcps::keyword::Opt_Projected
                            % pcps::keyword::Opt_Projected_TI
                            % pcps::keyword::Opt_Imaginary_Time
                            % pcps::keyword::Opt_Stochastic_Imaginary_Time
                            % pcps::keyword::Opt_Real_Time
                            % pcps::keyword::Opt_Full_CI
                            % pcps::keyword::Opt_PMC
                            % pcps::keyword::Opt_Dump_DMRG_Ints
                            % pcps::keyword::Opt_None
                           ).str() );

  // verify lattice type
  if (    _lattice_type != pcps::keyword::Lattice_Cartesian
       && _lattice_type != pcps::keyword::Lattice_Triangular
       && _lattice_type != pcps::keyword::Lattice_Icosidodecahedron )
    throw pcps::Exception( (boost::format("keyword \"lattice_type\" must be \"%s\", \"%s\", or \"%s\".")
                            % pcps::keyword::Lattice_Cartesian
                            % pcps::keyword::Lattice_Triangular
                            % pcps::keyword::Lattice_Icosidodecahedron
                           ).str() );

  // verify correlator type
  if (    _correlator_type != pcps::keyword::Correlator_Line
       && _correlator_type != pcps::keyword::Correlator_Square
       && _correlator_type != pcps::keyword::Correlator_Triangle )
    throw pcps::Exception( (boost::format("keyword \"correlator_type\" must be \"%s\", \"%s\", or \"%s\".")
                            % pcps::keyword::Correlator_Line
                            % pcps::keyword::Correlator_Square
                            % pcps::keyword::Correlator_Triangle
                           ).str() );

  // verify hamiltonian type
  if (   _hamiltonian_type != pcps::keyword::Ham_Hubbard
      && _hamiltonian_type != pcps::keyword::Ham_Heisenberg
      && _hamiltonian_type != pcps::keyword::Ham_File
      && _hamiltonian_type != pcps::keyword::Ham_PSI3
      && _hamiltonian_type != pcps::keyword::Ham_Molpro
      && _hamiltonian_type != pcps::keyword::Ham_Text_1212
      && _hamiltonian_type != pcps::keyword::Ham_Text_1122 )
    throw pcps::Exception("keyword \"hamiltonian_type\" must be \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", or \"%s\".")
                            % pcps::keyword::Ham_Hubbard
                            % pcps::keyword::Ham_Heisenberg
                            % pcps::keyword::Ham_File
                            % pcps::keyword::Ham_PSI3
                            % pcps::keyword::Ham_Molpro
                            % pcps::keyword::Ham_Text_1212
                            % pcps::keyword::Ham_Text_1122;

  // verify particle type
  if (    _particle_type != pcps::keyword::Part_Spin
       && _particle_type != pcps::keyword::Part_Spinless_Fermion
       && _particle_type != pcps::keyword::Part_Fermion )
    throw pcps::Exception( (boost::format("keyword \"particle_type\" must be \"%s\", \"%s\", or \"%s\".")
                            % pcps::keyword::Part_Spin
                            % pcps::keyword::Part_Spinless_Fermion
                            % pcps::keyword::Part_Fermion
                           ).str() );

  // verify reference
  if (    _particle_type == pcps::keyword::Part_Fermion
       && _ref_type != pcps::keyword::Ref_Uniform
       && _ref_type != pcps::keyword::Ref_RHF
       && _ref_type != pcps::keyword::Ref_AGP
       && _ref_type != pcps::keyword::Ref_Pairing
       && _ref_type != pcps::keyword::Ref_Pfaffian )
    throw pcps::Exception( (boost::format("Error in \"ref_type\" keyword.  Available fermion references are \"%s\", \"%s\", \"%s\", \"%s\", and \"%s\".")
                            % pcps::keyword::Ref_Uniform
                            % pcps::keyword::Ref_RHF
                            % pcps::keyword::Ref_AGP
                            % pcps::keyword::Ref_Pairing
                            % pcps::keyword::Ref_Pfaffian
                           ).str() );
  if (    _particle_type == pcps::keyword::Part_Spinless_Fermion
       && _ref_type != pcps::keyword::Ref_Uniform
       && _ref_type != pcps::keyword::Ref_RHF )
    throw pcps::Exception( (boost::format("Error in \"ref_type\" keyword.  Available spinless fermion references are \"%s\" and \"%s\".")
                            % pcps::keyword::Ref_Uniform
                            % pcps::keyword::Ref_RHF
                           ).str() );
  if (    _particle_type == pcps::keyword::Part_Spin
       && _ref_type != pcps::keyword::Ref_Uniform 
       && _ref_type != pcps::keyword::Ref_AGP
       && _ref_type != pcps::keyword::Ref_Pairing )
    throw pcps::Exception( (boost::format("Error in \"ref_type\" keyword.  Available spin references are \"%s\", \"%s\", and \"%s\".")
                            % pcps::keyword::Ref_Uniform
                            % pcps::keyword::Ref_AGP
                            % pcps::keyword::Ref_Pairing
                           ).str() );

  // verify correlator sources
  for (std::set<std::string>::const_iterator s = _corr_sources.begin(); s != _corr_sources.end(); s++)
    if (    *s != pcps::keyword::Corr_Source_None
         && *s != pcps::keyword::Corr_Source_Binary
         && *s != pcps::keyword::Corr_Source_Text
         && *s != pcps::keyword::Corr_Source_Classical
         && *s != pcps::keyword::Corr_Source_Lattice
         && *s != pcps::keyword::Corr_Source_All_Pair )
      throw pcps::Exception( (boost::format("keyword \"corr_sources\" may only contain \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", or \"%s\".")
                              % pcps::keyword::Corr_Source_None
                              % pcps::keyword::Corr_Source_Binary
                              % pcps::keyword::Corr_Source_Text
                              % pcps::keyword::Corr_Source_Classical
                              % pcps::keyword::Corr_Source_Lattice
                              % pcps::keyword::Corr_Source_All_Pair
                             ).str() );

  // verify orbital source
  if (    _orb_source != pcps::keyword::Orb_Source_Binary
       && _orb_source != pcps::keyword::Orb_Source_Text
       && _orb_source != pcps::keyword::Orb_Source_None )
    throw pcps::Exception( (boost::format("keyword \"orb_source\" must be \"%s\", \"%s\", or \"%s\".")
                            % pcps::keyword::Orb_Source_Binary
                            % pcps::keyword::Orb_Source_Text
                            % pcps::keyword::Orb_Source_None
                           ).str() );

  // verify agp weight source
  if (    _agp_source != pcps::keyword::AGP_Source_Binary
       && _agp_source != pcps::keyword::AGP_Source_Text
       && _agp_source != pcps::keyword::AGP_Source_RHF
       && _agp_source != pcps::keyword::AGP_Source_None )
    throw pcps::Exception( (boost::format("keyword \"agp_source\" must be \"%s\", \"%s\", \"%s\", or \"%s\".")
                            % pcps::keyword::AGP_Source_Binary
                            % pcps::keyword::AGP_Source_Text
                            % pcps::keyword::AGP_Source_RHF
                            % pcps::keyword::AGP_Source_None
                           ).str() );

  // verify pairing matrix source
  if (    _pairing_source != pcps::keyword::Pairing_Source_Binary
       && _pairing_source != pcps::keyword::Pairing_Source_Text
       && _pairing_source != pcps::keyword::Pairing_Source_Random
       && _pairing_source != pcps::keyword::Pairing_Source_AGP
       && _pairing_source != pcps::keyword::Pairing_Source_GHF
       && _pairing_source != pcps::keyword::Pairing_Source_None )
    throw pcps::Exception( (boost::format("keyword \"pairing_source\" must be \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", or \"%s\".")
                            % pcps::keyword::Pairing_Source_Binary
                            % pcps::keyword::Pairing_Source_Text
                            % pcps::keyword::Pairing_Source_Random
                            % pcps::keyword::Pairing_Source_AGP
                            % pcps::keyword::Pairing_Source_GHF
                            % pcps::keyword::Pairing_Source_None
                           ).str() );

  // verify move type
  if (    _particle_type == pcps::keyword::Part_Fermion
       && _move_type != pcps::keyword::Move_Standard
       && _move_type != pcps::keyword::Move_Exchange
       && _move_type != pcps::keyword::Move_Mix
       && _move_type != pcps::keyword::Move_DoubleAB    )
    throw pcps::Exception( (boost::format("for fermions, keyword \"move_type\" must be \"%s\", \"%s\", \"%s\", or \"%s\".")
                            % pcps::keyword::Move_Standard
                            % pcps::keyword::Move_Exchange
                            % pcps::keyword::Move_Mix
                            % pcps::keyword::Move_DoubleAB
                           ).str() );
  if (    _particle_type == pcps::keyword::Part_Spinless_Fermion
       && _move_type != pcps::keyword::Move_Standard    )
    throw pcps::Exception( (boost::format("for spinless fermions, keyword \"move_type\" must be \"%s\".")
                            % pcps::keyword::Move_Standard
                           ).str() );
  if (    _particle_type == pcps::keyword::Part_Spin
       && _move_type != pcps::keyword::Move_Standard    )
    throw pcps::Exception( (boost::format("for spins, keyword \"move_type\" must be \"%s\".")
                            % pcps::keyword::Move_Standard
                           ).str() );

  // verify particle spin
  if ( _particle_spin <= 0.0 || !pcps::is_half_integer(_particle_spin) )
    throw pcps::Exception("keyword \"particle_spin\" must be a positive half integer (0.5, 1.0, 1.5, ...)");
  if ( _particle_type == pcps::keyword::Part_Fermion && std::fabs(_particle_spin - 0.5) > 1.0e-9 )
    throw pcps::Exception("keyword \"particle_spin\" must be 0.5 for fermions");

  // verify z component of spin
  if (!pcps::is_half_integer(_z_spin))
    throw pcps::Exception("keyword \"z_spin\" must be a half integer (..., -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, ...)");

  // verify damping
  if (_damping < 0.0 || _damping >= 1.0)
    throw pcps::Exception("keyword \"damping\" must be in the range 0.0 <= damping < 1.0");

  // verify step_size
  if (_step_size <= 0.0)
    throw pcps::Exception("keyword \"step_size\" must be positive");

  // verify boundary type
  if (    _boundary != pcps::keyword::Boundary_Open
       && _boundary != pcps::keyword::Boundary_Periodic )
    throw pcps::Exception( (boost::format("keyword \"boundary\" must be \"%s\" or \"%s\".")
                            % pcps::keyword::Boundary_Open
                            % pcps::keyword::Boundary_Periodic
                           ).str() );

  // verify parallelism type
  if (    _parallel_type != pcps::keyword::Parallel_Site
       && _parallel_type != pcps::keyword::Parallel_Farm )
    throw pcps::Exception( (boost::format("keyword \"parallel_type\" must be \"%s\" or \"%s\".")
                            % pcps::keyword::Parallel_Site
                            % pcps::keyword::Parallel_Farm
                           ).str() );

  // verify scalar type
  if (    _scalar_type != pcps::keyword::Scalar_Real
       && _scalar_type != pcps::keyword::Scalar_Complex )
    throw pcps::Exception( (boost::format("keyword \"scalar_type\" must be \"%s\" or \"%s\".")
                            % pcps::keyword::Scalar_Real
                            % pcps::keyword::Scalar_Complex
                           ).str() );

  // verify number of threads
  if ( _nthreads <= 0 )
    throw pcps::Exception( (boost::format("keyword \"nthreads\" must be positive.")).str() );

  // verify arena block size
  if ( _arena_block_size < 0 )
    throw pcps::Exception( (boost::format("keyword \"arena_block_size\" must be non-negative.")).str() );

  // verify arena buffer size
  if ( _arena_buffer_size < 0 )
    throw pcps::Exception( (boost::format("keyword \"arena_buffer_size\" must be non-negative.")).str() );

  // verify that spin projection is allowed
  if ( !_project_sz && _particle_type != pcps::keyword::Part_Spin )
    throw pcps::Exception( (boost::format("keyword \"project_sz\" may be false ONLY if \"particle_type\" is set to %s.")
                            % pcps::keyword::Part_Spin
                           ).str() );

  // verify that AGP weight optimization is allowed
  if (    _optimize_agp_weights
       && _ref_type != pcps::keyword::Ref_AGP     )
    throw pcps::Exception( (boost::format("keyword \"optimize_agp_weights = true\" requires the \"%s\" reference type.")
                            %  pcps::keyword::Ref_AGP
                           ).str() );

  // verify that orbital coefficient optimization is allowed
  if (    _optimize_orb_coeffs
       && _ref_type != pcps::keyword::Ref_RHF
       && _ref_type != pcps::keyword::Ref_AGP     )
    throw pcps::Exception( (boost::format("keyword \"optimize_orb_coeffs = true\" requires either the \"%s\" or \"%s\" reference type.")
                            %  pcps::keyword::Ref_RHF
                            %  pcps::keyword::Ref_AGP
                           ).str() );

  // verify that pairing matrix optimization is allowed
  if (    _optimize_pairing_matrix
       && _ref_type != pcps::keyword::Ref_Pairing
       && _ref_type != pcps::keyword::Ref_Pfaffian )
    throw pcps::Exception( (boost::format("keyword \"optimize_pairing_matrix = true\" requires the \"%s\" or \"%s\" reference type.")
                            %  pcps::keyword::Ref_Pairing
                            %  pcps::keyword::Ref_Pfaffian
                           ).str() );

  // verify that pairing matrix symmetrization is allowed
  if ( _symmetric_pairing && ( _ref_type != pcps::keyword::Ref_Pairing || _optimize_pairing_matrix == false ) )
    throw pcps::Exception(
      (boost::format("keyword \"symmetric_pairing = true\" requires \"optimize_pairing_matrix = true\" and the \"%s\" reference type.")
        %  pcps::keyword::Ref_Pairing
      ).str() );

  // verify that a spin to fermion dump is allowed
  if ( _spin_to_fermion_dump && ( _particle_type != pcps::keyword::Part_Spin || std::fabs(_particle_spin - 0.5) > 1.0e-9 ) )
    throw pcps::Exception( (boost::format("keyword \"spin_to_fermion_dump = true\" requires spin 1/2 spins.")).str() );

  // verify that the projector monte carlo value of tau is positive
  if ( _pmc_tau <= 0.0 )
    throw pcps::Exception( (boost::format("keyword \"pmc_tau\" must be positive.")).str() );

  // verify that the two electron integral screeing threshold is non-negative
  if ( _tei_screening < 0.0 )
    throw pcps::Exception( (boost::format("keyword \"tei_screening\" must be non-negative.")).str() );

  // verify that the number of walkers per process is positive
  if ( _walkers_per_process <= 0 )
    throw pcps::Exception( (boost::format("keyword \"walkers_per_process\" must be positive.")).str() );

  // verify that the population control frequency is positive
  if ( _pop_control_freq <= 0 )
    throw pcps::Exception( (boost::format("keyword \"pop_control_freq\" must be positive.")).str() );

  // verify that the stochastic reconfiguration disk block size is positive
  if ( _sr_disk_block_size <= 0 )
    throw pcps::Exception( (boost::format("keyword \"sr_disk_block_size\" must be positive.")).str() );

}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Prepares some data that is derived from the user's input options.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::Input::initialize_derived_data() {

  // if no correlator sources were specified, set the sources to "none"
  if (_corr_sources.size() == 0)
    _corr_sources.insert(pcps::keyword::Corr_Source_None);

  // determine the number of sites
  if (_lattice_type == pcps::keyword::Lattice_Cartesian || _lattice_type == pcps::keyword::Lattice_Triangular)
    _nsites = _lattice_length_x * _lattice_length_y * _lattice_length_z;
  else if (_lattice_type == pcps::keyword::Lattice_Icosidodecahedron)
    _nsites = 30;
  else
    throw pcps::Exception("unknown lattice_type");

  // if the particles are fermions, double the number of sites to account for alpha and beta
  if (_particle_type == pcps::keyword::Part_Fermion)
    _nsites *= 2;

  // ensure that the working directory ends in a slash
  if (_workdir.size() > 0) {
    if (_workdir[_workdir.size()-1] != '/')
      _workdir.push_back('/');
  } else {
    throw pcps::Exception("keyword \"workdir\" must not be blank");
  }

  // determine the number of configurations per site
  if (    _particle_type == pcps::keyword::Part_Fermion
       || _particle_type == pcps::keyword::Part_Spinless_Fermion )
    _configs_per_site = 2;
  else if ( _particle_type == pcps::keyword::Part_Spin )
    _configs_per_site = pcps::round(2 * _particle_spin) + 1;
  else
    throw pcps::Exception("unexpected particle type");

  // determine the number of agp weights
  if (      _particle_type == pcps::keyword::Part_Fermion )
    _n_agp_weights = _nsites/2;
  else if ( _particle_type == pcps::keyword::Part_Spin )
    _n_agp_weights = _nsites;
  else
    _n_agp_weights = 0;

  // determine the number of restricted orbital coefficients
  if (      _particle_type == pcps::keyword::Part_Fermion )
    _n_rest_orb_coeff = _nsites*_nsites/4;
  else if ( _particle_type == pcps::keyword::Part_Spin )
    _n_rest_orb_coeff = _nsites*_nsites;
  else
    _n_rest_orb_coeff = 0;

  // determine the number of variables in the pairing matrix
  if ( _ref_type == pcps::keyword::Ref_Pfaffian ) {
    if (      _particle_type == pcps::keyword::Part_Fermion)
      _n_pairing_variables = _nsites*(_nsites-1)/2;
    else if ( _particle_type == pcps::keyword::Part_Spin )
      _n_pairing_variables = 2*_nsites*(2*_nsites-1)/2;
    else
      _n_pairing_variables = 0;
  } else {
    if (      _particle_type == pcps::keyword::Part_Fermion)
      _n_pairing_variables = _nsites*_nsites/4;
    else if ( _particle_type == pcps::keyword::Part_Spin )
      _n_pairing_variables = _nsites*_nsites;
    else
      _n_pairing_variables = 0;
  }

  // if requested, use the current time for the global random seed
  if (_global_seed < 0)
    _global_seed = int( std::time(NULL) );
  if (_global_seed < 0)
    _global_seed = -_global_seed;

}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Splits a string into pieces using the supplied deliminators.
///
/// \param[in]     str           The string to be split.
/// \param[in]     deliminators  Characters used to deliminate the sections the string is to
///                              be split into.
///
/// \return A vector holding the sections of the split string.
///
///////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<std::string> pcps::split_string(const std::string &str, const std::string &deliminators) {

  // Initialize the return value
  std::vector<std::string> retval;

  // create a set of the deliminators
  std::set<char> delim_set;
  for (int i = 0; i < deliminators.size(); i++)
    delim_set.insert(deliminators.at(i));

  // Loop through the string searching for deliminating characters
  int i1 = 0;
  for (int i2 = 0; i2 < str.size(); i2++)
    // When the deliminating character is found, add a new string
    // to the return value consisting of all characters between the
    // previous and current deliminating characters
    if (delim_set.count(str.at(i2)))
    {
      if (i2 > i1)
        retval.push_back(str.substr(i1, i2-i1));
      i1 = i2 + 1;
    }

  // Add a string to the return value consisting of any characters between
  // the last deliminator and the end of the string
  if (i1 < str.size())
    retval.push_back(str.substr(i1, str.size()-i1));

  // return the vector of the split string
  return retval;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Sets a member's value using the remainder of the input command after the member name
///          has been read.
///
///          This is the default template behavior.
///          There are more specialized versions of this function for bools, vectors, and sets.
///
/// \param[in]     member_name  name of the member being set
/// \param[in,out] cmd_ss       string stream holding the command read from the input file
/// \param[out]    member       the member whose value will be set
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::Input::set_member(const std::string & member_name,
                                               std::stringstream & cmd_ss,
                                               T & member) {

  // get the member's value
  cmd_ss >> member;

  // if there was no argument, raise an error
  if (cmd_ss.eof())
    throw pcps::Exception("no argument given for member \"%s\"") % member_name;

  // make sure there isn't anything else in the command but whitespace
  std::string extra;
  cmd_ss >> std::skipws >> extra;
  if (!cmd_ss.eof())
    throw pcps::Exception("too many arguments given for input keyword \"%s\"") % member_name;

}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Sets a boolean member's value using the remainder of the input command
///          after the member name has been read.
///
/// \param[in]     member_name  name of the member being set
/// \param[in,out] cmd_ss       string stream holding the command read from the input file
/// \param[out]    member       the member whose value will be set
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::Input::set_member(const std::string & member_name,
                             std::stringstream & cmd_ss,
                             bool & member) {

  // use the string function to extract "true" or "false"
  std::string val;
  this->set_member(member_name, cmd_ss, val);

  // process the value
  if (val == "true")
    member = true;
  else if (val == "false")
    member = false;
  else
    throw pcps::Exception("the argument for boolean member \"%s\" must be \"true\" or \"false\"") % member_name;

}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Sets a vector member's value using the remainder of the input command
///          after the member name has been read.
///
/// \param[in]     member_name  name of the member being set
/// \param[in,out] cmd_ss       string stream holding the command read from the input file
/// \param[out]    member       the member whose value will be set
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::Input::set_member(const std::string & member_name,
                                               std::stringstream & cmd_ss,
                                               std::vector<T> & member) {

  // empty the vector
  member.clear();

  // populate the vector with the remaining elements of the command
  while (true) {

    // get the next element, skipping whitespace
    T x;
    cmd_ss >> std::skipws >> x;

    // if the end of the command was reached, then we are finished
    if (cmd_ss.eof()) break;

    // append the vector with the element
    member.push_back(x);

  }

}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Sets a set member's value using the remainder of the input command
///          after the member name has been read.
///
/// \param[in]     member_name  name of the member being set
/// \param[in,out] cmd_ss       string stream holding the command read from the input file
/// \param[out]    member       the member whose value will be set
///
///////////////////////////////////////////////////////////////////////////////////////////////////
template<class T> void pcps::Input::set_member(const std::string & member_name,
                                               std::stringstream & cmd_ss,
                                               std::set<T> & member) {

  // empty the set
  member.clear();

  // populate the set with the remaining elements of the command
  while (true) {

    // get the next element, skipping whitespace
    T x;
    cmd_ss >> std::skipws >> x;

    // if the end of the command was reached, then we are finished
    if (cmd_ss.eof()) break;

    // add the element to the set
    member.insert(x);

  }

}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Broadcasts an input command and its line number to the slave processes.
///          This function should only be called by the root process.
///
/// \param[in]     cmd        the command read from the input file
/// \param[in]     line_num   the input file line number on which the command ended
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::Input::bcast_input_command(std::vector<char> & cmd, int line_num) {

  // get MPI info
  const MPI::Comm & comm = MPI::COMM_WORLD;
  const int nproc = comm.Get_size();
  const int myrank = comm.Get_rank();

  // broadcast the command and line number
  int length = cmd.size();
  comm.Bcast(&length, 1, MPI::INT, 0);
  comm.Bcast(&line_num, 1, MPI::INT, 0);
  if (length > 0)
    comm.Bcast(&cmd.at(0), length, MPI::CHAR, 0);

}

///////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief   Reads the %keyword of a user input command and sets the corresponding member's value.
///
/// \param[in]     cmd        the command read from the input file
/// \param[in]     line_num   the input file line number on which the command ended
///
///////////////////////////////////////////////////////////////////////////////////////////////////
void pcps::Input::process_input_command(const std::vector<char> & cmd, const int line_num) {

  // process the command, watching for errors
  try {

    // convert the command into a string stream,
    // padding with a space to ensure that >> operators do not set eof prematurely
    std::stringstream ss( std::string(cmd.begin(), cmd.end()).append(" ") );

    // get the name of the member whose value the command is setting
    std::string member_name;
    ss >> member_name;

    // if the line was empty, there is nothing to do
    if (ss.eof()) return;

    // if the member name is somehow empty, raise an error
    if (member_name.empty())
      throw pcps::Exception("member_name is empty");

    // set the corresponding data member
    if      (member_name == "lattice_type") this->set_member(member_name, ss, _lattice_type);
    else if (member_name == "lattice_length_x") this->set_member(member_name, ss, _lattice_length_x);
    else if (member_name == "lattice_length_y") this->set_member(member_name, ss, _lattice_length_y);
    else if (member_name == "lattice_length_z") this->set_member(member_name, ss, _lattice_length_z);
    else if (member_name == "n_cor_orb") this->set_member(member_name, ss, _n_cor_orb);
    else if (member_name == "n_act_orb") this->set_member(member_name, ss, _n_act_orb);
    else if (member_name == "nparticles") this->set_member(member_name, ss, _nparticles);
    else if (member_name == "bfgs_length") this->set_member(member_name, ss, _bfgs_length);
    else if (member_name == "dump_freq") this->set_member(member_name, ss, _dump_freq);
    else if (member_name == "print_freq") this->set_member(member_name, ss, _print_freq);
    else if (member_name == "arena_block_size") this->set_member(member_name, ss, _arena_block_size);
    else if (member_name == "arena_buffer_size") this->set_member(member_name, ss, _arena_buffer_size);
    else if (member_name == "nthreads") this->set_member(member_name, ss, _nthreads);
    else if (member_name == "global_seed") this->set_member(member_name, ss, _global_seed);
    else if (member_name == "walkers_per_process") this->set_member(member_name, ss, _walkers_per_process);
    else if (member_name == "pop_control_freq") this->set_member(member_name, ss, _pop_control_freq);
    else if (member_name == "sr_disk_block_size") this->set_member(member_name, ss, _sr_disk_block_size);
    else if (member_name == "z_spin") this->set_member(member_name, ss, _z_spin);
    else if (member_name == "correlator_size") this->set_member(member_name, ss, _correlator_size);
    else if (member_name == "correlator_type") this->set_member(member_name, ss, _correlator_type);
    else if (member_name == "particle_type") this->set_member(member_name, ss, _particle_type);
    else if (member_name == "ref_type") this->set_member(member_name, ss, _ref_type);
    else if (member_name == "scalar_type") this->set_member(member_name, ss, _scalar_type);
    else if (member_name == "boundary") this->set_member(member_name, ss, _boundary);
    else if (member_name == "hamiltonian_type") this->set_member(member_name, ss, _hamiltonian_type);
    else if (member_name == "hubbard_t") this->set_member(member_name, ss, _hubbard_t);
    else if (member_name == "hubbard_u") this->set_member(member_name, ss, _hubbard_u);
    else if (member_name == "heisenberg_b") this->set_member(member_name, ss, _heisenberg_b);
    else if (member_name == "particle_spin") this->set_member(member_name, ss, _particle_spin);
    else if (member_name == "max_macro_loop") this->set_member(member_name, ss, _max_macro_loop);
    else if (member_name == "max_nr_iter") this->set_member(member_name, ss, _max_nr_iter);
    else if (member_name == "max_ac_length") this->set_member(member_name, ss, _max_ac_length);
    else if (member_name == "max_hf_iter") this->set_member(member_name, ss, _max_hf_iter);
    else if (member_name == "max_cg_iter") this->set_member(member_name, ss, _max_cg_iter);
    else if (member_name == "max_cg_macro_iter") this->set_member(member_name, ss, _max_cg_macro_iter);
    else if (member_name == "damping") this->set_member(member_name, ss, _damping);
    else if (member_name == "step_size") this->set_member(member_name, ss, _step_size);
    else if (member_name == "max_grad_norm") this->set_member(member_name, ss, _max_grad_norm);
    else if (member_name == "overlap_shift") this->set_member(member_name, ss, _overlap_shift);
    else if (member_name == "cg_thresh") this->set_member(member_name, ss, _cg_thresh);
    else if (member_name == "uhf_bias") this->set_member(member_name, ss, _uhf_bias);
    else if (member_name == "fci_shift") this->set_member(member_name, ss, _fci_shift);
    else if (member_name == "trial_energy") this->set_member(member_name, ss, _trial_energy);
    else if (member_name == "pmc_tau") this->set_member(member_name, ss, _pmc_tau);
    else if (member_name == "tei_screening") this->set_member(member_name, ss, _tei_screening);
    else if (member_name == "init_config_thresh") this->set_member(member_name, ss, _init_config_thresh);
    else if (member_name == "uhf_desegragation") this->set_member(member_name, ss, _uhf_desegragation);
    else if (member_name == "uhf_bias_decay") this->set_member(member_name, ss, _uhf_bias_decay);
    else if (member_name == "energy_tolerance") this->set_member(member_name, ss, _energy_tolerance);
    else if (member_name == "verbose_print") this->set_member(member_name, ss, _verbose_print);
    else if (member_name == "do_perturb") this->set_member(member_name, ss, _do_perturb);
    else if (member_name == "properties") this->set_member(member_name, ss, _properties);
    else if (member_name == "fix_classical") this->set_member(member_name, ss, _fix_classical);
    else if (member_name == "join_trotter_clusters") this->set_member(member_name, ss, _join_trotter_clusters);
    else if (member_name == "random_correlators") this->set_member(member_name, ss, _random_correlators);
    else if (member_name == "optimize_corrs") this->set_member(member_name, ss, _optimize_corrs);
    else if (member_name == "optimize_agp_weights") this->set_member(member_name, ss, _optimize_agp_weights);
    else if (member_name == "optimize_orb_coeffs") this->set_member(member_name, ss, _optimize_orb_coeffs);
    else if (member_name == "optimize_pairing_matrix") this->set_member(member_name, ss, _optimize_pairing_matrix);
    else if (member_name == "symmetric_pairing") this->set_member(member_name, ss, _symmetric_pairing);
    else if (member_name == "spin_to_fermion_dump") this->set_member(member_name, ss, _spin_to_fermion_dump);
    else if (member_name == "project_sz") this->set_member(member_name, ss, _project_sz);
    else if (member_name == "precondition_sr") this->set_member(member_name, ss, _precondition_sr);
    else if (member_name == "sr_use_disk") this->set_member(member_name, ss, _sr_use_disk);
    else if (member_name == "slightly_random_orbs") this->set_member(member_name, ss, _slightly_random_orbs);
    else if (member_name == "active_energy_only") this->set_member(member_name, ss, _active_energy_only);
    else if (member_name == "s_half_orth") this->set_member(member_name, ss, _s_half_orth);
    else if (member_name == "spin_pair_correlations") this->set_member(member_name, ss, _spin_pair_correlations);
    else if (member_name == "ssss_correlations") this->set_member(member_name, ss, _ssss_correlations);
    else if (member_name == "spin_triple_products") this->set_member(member_name, ss, _spin_triple_products);
    else if (member_name == "spin_triangle_3sz") this->set_member(member_name, ss, _spin_triangle_3sz);
    else if (member_name == "workdir") this->set_member(member_name, ss, _workdir);
    else if (member_name == "optimization") this->set_member(member_name, ss, _optimization);
    else if (member_name == "move_type") this->set_member(member_name, ss, _move_type);
    else if (member_name == "orb_source") this->set_member(member_name, ss, _orb_source);
    else if (member_name == "agp_source") this->set_member(member_name, ss, _agp_source);
    else if (member_name == "pairing_source") this->set_member(member_name, ss, _pairing_source);
    else if (member_name == "parallel_type") this->set_member(member_name, ss, _parallel_type);
    else if (member_name == "sample_length") this->set_member(member_name, ss, _sample_length);
    else if (member_name == "burn_in_length") this->set_member(member_name, ss, _burn_in_length);
    else if (member_name == "properties_sample_length") this->set_member(member_name, ss, _properties_sample_length);
    else if (member_name == "begin_true_projector") this->set_member(member_name, ss, _begin_true_projector);
    else if (member_name == "init_config") this->set_member(member_name, ss, _init_config);
    else if (member_name == "corr_sources") this->set_member(member_name, ss, _corr_sources);

    // if the member name was not found, raise an error
    else
      throw pcps::Exception("unknown input keyword \"%s\"") % member_name;

  // if an error occured, edit the error message to include the line number
  } catch (std::exception & e) {

    throw pcps::Exception("problem processing command ending on line %i:  %s") % line_num % e.what();

  }

}
